package com.lpj.base.id.generator.service.impl.provider;

import com.lpj.base.id.generator.service.impl.bean.IdMeta;
import com.lpj.base.id.generator.service.impl.bean.MachineIdEntity;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通过配置文件可配置的机器id提供者
 */
public class PropertyMachineIdProvider implements MachineIdProvider {
    private final Map<Long, MachineIdEntity> machineIdsMap;
    private static PropertyMachineIdProvider instance;

    private PropertyMachineIdProvider(List<Long> machineIds, Integer type) {
        if (instance != null)
            throw new RuntimeException("Cannot instantiate more than once");
        if (CollectionUtils.isEmpty(machineIds))
            throw new RuntimeException("machineIds can not be null");
        this.machineIdsMap = new ConcurrentHashMap<>();
        long currentTime = System.currentTimeMillis();
        machineIds.forEach(item -> {
            MachineIdEntity machineIdEntity = new MachineIdEntity(item);
            machineIdEntity.setUserTimestamp(currentTime);
            machineIdEntity.setModifiedOn(currentTime);
            machineIdEntity.setType(type);
            machineIdEntity.setLastTimestamp(0L);
            this.machineIdsMap.put(item, machineIdEntity);
        });
    }

    public static PropertyMachineIdProvider getInstance(List<Long> machineIds, Integer type) {
        if (instance == null) {
            synchronized (PropertyMachineIdProvider.class) {
                if (instance == null) {
                    instance = new PropertyMachineIdProvider(machineIds, type);
                }
            }
        }
        return instance;
    }

    public long getMachineId(IdMeta idMeta) {
        if (this.machineIdsMap.isEmpty())
            throw new RuntimeException("machineIds can not be null");
        long currentTimeMillis = System.currentTimeMillis();
        for (Map.Entry<Long, MachineIdEntity> entityEntry : this.machineIdsMap.entrySet()) {
            MachineIdEntity machineIdEntity = entityEntry.getValue();
            if (machineIdEntity.getLastTimestamp() == 0L)
                return entityEntry.getKey();
            if (machineIdEntity.getLastTimestamp() < currentTimeMillis) {
                machineIdEntity.setLastTimestamp(0L);
                return entityEntry.getKey();
            }
        }
        throw new RuntimeException("System time called back multiple times, resulting in no machine ID available");
    }

    @Override
    public void setBadMachineId(long machineId, long lastTimestamp) {
        MachineIdEntity machineIdEntity = this.machineIdsMap.get(machineId);
        if (machineIdEntity == null)
            throw new RuntimeException("machineId not exists,machineId:" + machineId);
        machineIdEntity.setLastTimestamp(lastTimestamp);
    }

}
